import axios from 'axios'
/**
 * vue-ui 通用工具类库
 * 包含：
 * EsConfig: 全局配置信息助手类
 * EsLoginState: 全局登录状态助手类
 * EsUtils: 全局通用工具类
 * getAxios: 获取标准化 Axios 实例对象
 * myAxios: 已经标准化的 axios 实例对象
 * 常用方法：
 * this.gpEsConfig.setResourceUrl() # 切换本地资源和远程资源
 * this.gpLoginState.isLogin() # 判断登录状态
 * this.gpLoginState.setToken(token) # 保存已登录的 token 信息
 */
/**
 * 全局配置信息
 */
class EsConfig {
    // 注意：ES6明确规定，Class内部只有静态方法，没有静态属性。所以要把静态属性改为静态方法。ES7 才支持静态属性，可用 babel 编译。
    // 推广渠道标识
    channelName = "";
    // 应用市场标识 wxmp=微信小程序&app=应用APP&h5=H5应用&huawei=华为应用市场&xiaomi=小米应用市场&vivo=Vivo应用市场&oppo=Oppo应用市场
    appMarket = "h5";
    // 登录接口提供商 weixin=App微信登录&wxmp=微信小程序登录&mobile=手机号登录&test=模拟测试登录
    loginProvider = "mobile";
    // 是否微信小程序环境
    isEnvOfWeixinMP = false;
    // 是否 APP 环境（HTML5 Plus）
    isEnvOfPlus = false;
    // 是否 H5 环境
    isEnvOfH5 = true;
    // 是否显示电池占位区（微信小程序不用，APP和H5需要）
    showTopStatusBar = true;
    // 登录会话缓存名称
    tokenName = "admin_token";
    // 环境模式(debug=调试环境&dev=开发环境&prod=生产环境)
    envMode = 'dev'
    // 开发服务器域名（用于在线图片）
    domain = "http://abc.lh/";
    // domain = "http://tvadmin.lh/";
    // 开发服务器接口地址
    apiBaseUrl = "http://abc.lh/index.php";
    // apiBaseUrl = "http://tvadmin.lh/index.php";
    // 默认头像图标
    defaultAvatarIcon = "/static/images/default.png";
    // 支付宝支付名称 alipay=支付宝App支付&alipayh5=支付宝H5支付
    payNameOfAlipay = "alipayh5";
    // 微信支付名称 wxpay=微信App支付&wxpaymp=微信小程序支付&wxpayh5=微信H5支付
    payNameOfWxpay = "wxpayh5";
    // 支付间隔时间
    payInterval = 10;
    // 短信发送间隔时间（秒）
    smsInterval = 30;
    // 短信测试验证码（超级验证码）
    smsTestingCaptcha = "0609";
    // 是否使用模拟数据（Mock.js）
    useMockData = false;
    // http请求超时时间（毫秒）
    httpTimeout = 60000;

    constructor(option) {
        if (!option || typeof (option) != 'object') {
            return;
        }
        for (let k in option) {
            if (this[k]) {
                this[k] = option[k];
            }
        }
    }

    // 静态实例对象
    static instance = null;
    static init(option) {
        EsConfig.instance = new EsConfig(option);
        return LoginState.instance;
    }
    static me() {
        return EsConfig.instance;
    }

    /**
     * 设置资源URL地址（切换本地路径为远程路径）
     * @example setResourceUrl('/uploads/2021/1119/d3974335bb7fc1ebe065f3c5c0dfa84f.png')
     * @example setResourceUrl('/static/h5app/images/icon.png')
     */
    setResourceUrl(path) {
        if (!path) {
            return '';
        }
        if (path.indexOf("/static/" == 0)) {
            // return path;
        }
        return path.indexOf('http') == 0 ? path : (this.domain + path);
    }

    /**
     * 返回可用的头像地址
     */
    getAvatarImage(path) {
        if (!path || path.trim().length == 0) {
            return this.defaultAvatarIcon;
        }
        return this.setResourceUrl(path);
    }

    /**
     * 解析 Laravel 服务端的表单校验消息
     */
    parseValidateMessageOfLaravel(message) {
        if (typeof message == "object") {
            let messageArr = [];
            for (let k in message) {
                message[k].forEach((text) => {
                    messageArr.push(text);
                });
            }
            message = messageArr.join("<br />");
        }
        return message;
    }
}

/**
 * 用户登录信息公共类
 */
class LoginState {
    tokenName = '';
    constructor(tokenName) {
        this.tokenName = tokenName || 'admin_token';
    }

    // 静态实例对象
    static instance = null;
    static init(tokenName) {
        LoginState.instance = new LoginState(tokenName);
        return LoginState.instance;
    }
    static me() {
        return LoginState.instance;
    }

    /**
     * 保存登录会话信息
     */
    setToken(token) {
        window.sessionStorage.setItem(this.tokenName, token);
    }
    /**
     * 获取登录会话信息
     * 返回 string|null
     */
    getToken() {
        return window.sessionStorage.getItem(this.tokenName);
    }
    /**
     * 清除登录会话信息
     */
    clearToken() {
        window.sessionStorage.removeItem(this.tokenName);
    }
    /**
     * 判断登录状态
     */
    isLogin() {
        return this.getToken() ? true : false;
    }
}

/**
 * 通用工具类
 * @version 1.0.0
 * @method stdMessage(success, message, data = '', code = 0) 返回标准消息对象
 * @method random(min, max=0) 生成随机数
 */
class EsUtils {

    /**
     * 返回标准消息对象
     * @param {Boolean} $success 是否成功
     * @param {String} message 消息内容
     * @param {Object} data 数据内容
     * @param {Number} code 状态码 200=成功,401=未授权(未登录),403=禁止访问,500=服务器错误
     * @see https://tool.lu/httpcode/
     */
    static stdMessage(success, message, data = '', code = 0) {
        return {
            success: success,
            message: message,
            data: data,
            code: code,
        }
    }

    /**
     * 生成随机数
     * @example random(1,10) 生成 1...10之间的随机数（包括1和10）。random(10) 生成 0...10 之间的随机数
     */
    static random(min, max = 0) {
        if (max == 0) {
            max = min;
            min = 0;
        }
        return Math.round(Math.random() * (max - min)) + min;
    }

    /**
     * 返回数组中一个或多个随机的键
     */
    static arrayRandom(array, number = 1) {
        let idx = Math.floor((Math.random() * array.length));
        let data = [], array2 = [...array];
        // splice() 方法会改变原始数组
        for (let i = 0; i < number; i++) {
            data.push(array2.splice(idx, 1)[0]);
        }
        return number == 1 ? data[0] : data;
    }

    /**
     * 将数组打乱
     * 注意：此方法会改变原始数组！
     */
    static arrayShuffle(array) {
        // sort() 方法会改变原始数组！
        array.sort(() => {
            return Math.random() > 0.5 ? -1 : 1;
        })
        return array;
    }

    static arrayCopy(arr, result) {
        result = result || [];
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] instanceof Array) {
                result[i] = [];
                EsUtils.arrayCopy(arr[i], result[i]);
            } else {
                result[i] = arr[i];
            }
        }
        return result;
    }
    /**
     * 缩减字符串
     */
    static shortText(text, length = 10, lastLength = 0, ellipsis = '...') {
        if (!text || text.length < length) {
            return text;
        }
        if (lastLength == 0) {
            return text.substring(0, length) + ellipsis;
        }
        return text.substring(0, length - lastLength) + ellipsis + text.substr(-lastLength);
    }
    /**
     * 字节单位转换
     */
    static bytesToSize(size) {
        if (bytes === 0) {
            return '0 B';
        }
        var k = 1024, // or 1024
            sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            i = Math.floor(Math.log(bytes) / Math.log(k));

        return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
    }
    /**
     * 数字单位
     */
    static numberUnit(num = 0) {
        if (!num) {
            return 0;
        }
        num = parseInt(num);
        if (num > 10000) {
            return Math.round(num / 1000) + '万';
        }
        return num;
    }
    /**
     * 数值限制
     */
    static numberLimit(value, min = 0, max = 10) {
        if (isNaN(value)) {
            return min;
        }
        value = Number(value)
        if (value < min) {
            return min;
        }
        if (value > max) {
            return max;
        }
        return value;
    }
    // 对Date的扩展，将 Date 转化为指定格式的String 
    // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符， 
    // 年(y)可以用 1-4 个占位符，毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 
    // 例子： 
    // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 
    // (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 
    static timeFormat(fmt, text = null) {
        let date = text ? new Date(text) : new Date();
        //author: meizz 
        let o = {
            "M+": date.getMonth() + 1, //月份 
            "d+": date.getDate(), //日 
            "h+": date.getHours(), //小时 
            "m+": date.getMinutes(), //分 
            "s+": date.getSeconds(), //秒 
            "q+": Math.floor((date.getMonth() + 3) / 3), //季度 
            "S": date.getMilliseconds() //毫秒 
        };
        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (let k in o)
            if (new RegExp("(" + k + ")").test(fmt)) {
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k])
                    .length)));
            }
        return fmt;
    }
    // 获取浏览器参数
    static getQueryString(name, defval = null, url = '') {
        if (!url) {
            url = window.location.search;
        }
        if (!url) {
            return defval;
        }
        // ?id=1&image=awesome.jpg
        var params = url.substring(url.indexOf('?') + 1).split('&'),
            pair = [];
        for (let param of params) {
            pair = param.split('=');
            if (pair[0] == name) {
                // return pair[1];
                return decodeURIComponent(pair[1]);
            }
        }
        return defval;
    }
    // 获取cookie参数
    static getCookie(name, defval = null, cookie = '') {
        if (!cookie) {
            cookie = document.cookie;
        }
        if (!cookie) {
            return defval;
        }
        // cookie1=value; cookie2=value; cookie3=value;
        var params = cookie.split(';'),
            pair = [];
        for (let param of params) {
            pair = param.split('=');
            if (pair[0].trim() == name) {
                return pair[1];
            }
        }
        return defval;
    }
    /**
     * 复制到剪贴板
     */
    static copyText(text) {
        let input = document.createElement('input');
        input.value = text;
        document.body.appendChild(input);
        input.select();
        document.execCommand('copy');
        document.body.removeChild(input);
    }
}


/**
 * 创建自定义 Axios 实例
 * 是否启用 mock 数据（如果使用 mock 数据，则 url 不能拼接 BaseUrl，否则路径会匹配不上）
 * process.env.VUE_APP_API_BASE_URL // .env 文件主要用于多人协同的团队开发环境，我是一个人全栈，所以不使用 .env 文件，省得每次更改都要重新编译。。。
 */
function getAxios(baseURL = '', timeout = 60000, fnGetToken = null) {
    const myAxios = axios.create({
        baseURL: baseURL,
        timeout: timeout,
    })

    // axios 添加请求拦截
    myAxios.interceptors.request.use(config => {
        if (typeof (fnGetToken) == 'function') {
            const token = fnGetToken();
            if (token && typeof (token) == "string") {
                config.headers.Authorization = token;
            }
        }
        console.log('添加请求拦截', config.headers.Authorization);
        return config;
    })

    // axios 添加输出拦截
    myAxios.interceptors.response.use(response => {
        console.log('添加输出拦截', response);
        if (response.data.success == undefined) {
            return Promise.reject(EsUtils.stdMessage(false, "服务器返回的数据格式错误"))
        }
        return Promise.resolve(response.data);
    }, error => {
        return Promise.reject(EsUtils.stdMessage(false, error.message))
    })
    return myAxios;
}

// 获取标准化的 axios 示例（配置了拦截请求和输出请求）
const myAxios = getAxios(EsConfig.apiBaseUrl, EsConfig.httpTimeout, () => { LoginState.me().getToken() });

/**
 * 发起上传文件的请求并返回标准消息格式
 */
async function stdUploadFile(files, url = '/api/admin/uploader', params = {}) {
    let formData = new FormData(),
        isMultiple = files.length > 1;
    formData.append("is_multiple", isMultiple ? 1 : 0);
    if (isMultiple) {
        for (let file of files) {
            if (typeof file == "object") {
                formData.append("file[]", file);
            }
        }
    } else {
        formData.append("file", files[0]);
    }
    for (let k in params) {
        formData.set(k, params[k]);
    }
    const msgInfo = await myAxios.post(url, formData);
    console.log("[上传文件] msgInfo=", msgInfo);
    if (msgInfo.success && msgInfo.data instanceof Array) {
        let paths = [];
        msgInfo.data.forEach((row) => {
            paths.push(row.path);
        });
        msgInfo.data = paths.join(",")
    }
    return msgInfo;
}

/**
 * 发送短信验证码
 * @param {String} mobile
 * @param {String} event 事件类型，如：update_mobile
 */
async function stdSendSMS(mobile, event, isDebug = 0, url = '/api/sms/send') {
    if (mobile.length != 11) {
        return EsUtils.stdMessage(false, '手机号码无效')
    }
    let data = {
        mobile: mobile,
        event: event,
        isDebug: isDebug,
    };
    const msgInfo = await myAxios(url, data, 'POST');
    console.log('[发送短信验证码] data=', data, ' msgInfo=', msgInfo)
    return msgInfo;
}


/**
 * Vue.use(vui365) 安装函数
 * see: [Vue.use()用法](https://www.cnblogs.com/xiao1314/p/14426569.html)
 */
function install(Vue, option) {
    //  注册组件
    // Vue.component("CdButton", getComponentOfCdButton())
    // 全局属性使用 gp 前缀。gp = global prototype 的意思。
    // Vue.config.globalProperties.gpEsConfig = EsConfig;
    Vue.prototype.vuiEsConfig = EsConfig.init(option);
    Vue.prototype.vuiEsUtils = EsUtils;
    Vue.prototype.vuiLoginState = LoginState.init(option.tokenName);
    Vue.prototype.vuiAjax = myAxios;
    Vue.prototype.vuiUploadFile = stdUploadFile;
    Vue.prototype.vuiUploadFile = stdSendSMS;
}
export {
    install,
    EsConfig,
    EsUtils,
    LoginState,
    getAxios,
    myAxios,
    stdUploadFile,
    stdSendSMS,
}